<template>
  <div class="hello">
    <h1>vue3 el-table多级表头收缩扩展的实现</h1>
  </div>
  <div class="box">
    <el-table class="table default-margin" :data="tableData.value" :height="400" style="width: 100%" row-key="id"
      @header-click.self="headerClick" :header-cell-class-name="headerCellClassName" :row-class-name="tableRowClassName"
      :cell-style="cellStyle">
      <template v-for="item in tableheader.value" :key='item.id'>
        <template v-if="item.show">
          <!-- 如果存在子集，说明是多级表头 -->
          <el-table-column v-if="item.children" :label="item.label" :fixed="item.fixed">
            <el-table-column v-for="child in item.children" :prop="child.prop" :label="child.label" :width="child.width"
              :key='child.id' align="right" />
          </el-table-column>
          <el-table-column v-else :prop="item.prop" :label="item.label" :width="item.width" :fixed="item.fixed" />
        </template>
      </template>
      <!-- 空数据状态的插槽 -->
      <template #empty>
        <el-empty class="el-empty-table" :image-size="100" description='暂无数据'></el-empty>
      </template>
    </el-table>
  </div>
</template>
<script lang="ts" setup>
import { reactive, toRaw, onMounted } from 'vue'
import data from "./data.js";
onMounted(() => {
  console.log(data)
  // 假设调用接口获取到了数据
  tableheader.value = data.tableheader
  tableData.value = data.tableData
})
interface tableHeaderStatement {
  type: string, // 关联表头的标识
  type_children: string, // 表头子集标识
  show: boolean, // 是否要显示表头
  clickState: boolean, // 用于标记表头的点击状态
  fixed: boolean, // 表头是否需要悬浮
  width: string, // 表格宽度
  color: string // 表头颜色
  label: string, // 表头名称
  prop: string, // 表头对应数据字段名
  children: [{
    id: string,
    width: string,
    color: string,
    prop: string,
    label: string
  }]
}
// 表头数据
const tableheader: { value: Array<tableHeaderStatement> } = reactive({ value: [] })
// 表行数据
const tableData: { value: Array<object> } = reactive({ value: [] })
// 动态绑定表头颜色
const headerCellClassName = ({ column, rowIndex }: { column: { label: string, property: string }, rowIndex: number }) => {
  let tableheaderArr = toRaw(tableheader.value)
  if (rowIndex == 0) {// 处理表格第一行
    let label = column.label
    let index = tableheaderArr.findIndex(e => e.label == label)
    if (index == -1) return
    if (tableheaderArr[index].type_children) { // 如果存在子集，添加对应状态箭头符号
      if (tableheaderArr[index].clickState) {
        return tableheaderArr[index].color + ' header-open'
      } else {
        return tableheaderArr[index].color + ' header-close'
      }
    }
    return tableheaderArr[index].color
  } else if (rowIndex == 1) {    // 处理表格第二行
    let property = column.property
    let color = ''
    tableheaderArr.forEach(e => {
      if (e.children) {
        e.children.forEach(ele => {
          if (ele.prop == property) color = ele.color
        })
      }
    })
    return color
  }
}
// 动态绑定表行颜色
const tableRowClassName = (row: { row: { row_color: string } }) => {
  // console.log(row)
  if (row.row.row_color) return row.row.row_color
}
// 给某些单元格添加颜色
const cellStyle = (row: { column: { label: string, property: string }, row: Record<string, string> }) => {
  // console.log(row)
  let label = row.column.label
  let property = row.column.property
  if (label == "数据4") { // 根据自己的表名修改
    let probability = parseInt(row.row[property])
    if (probability >= 80) return { color: 'red !important' } // 如果完成率大于80标红
  }
}
// 点击表头
const headerClick = (e: { label: string }) => {
  // console.log(e)
  let label = e.label
  let tableheaderArr = toRaw(tableheader.value) // toRaw可以让数据不被时时监听，提升性能，JSON化也可以
  let clickIndex = tableheaderArr.findIndex(e => e.label == label) // 点击表单的下标 
  if (clickIndex == -1) return // 如果点击的不是第一行的表头
  let clickState = tableheaderArr[clickIndex].clickState // clickState用于判断表头是否点击过，打开过
  const type_children = tableheaderArr[clickIndex].type_children // 当前被点击的表头的子节点
  let indexChildrenArr: number[] = [] // 存放需要关闭或打开的节点下标
  let showMark: boolean // 用来标记是show要修改的状态
  if (clickState) {
    // console.log('关闭操作')
    showMark = false
    if (type_children) { // 如果点击的表头存在子节点
      let eachAllChildrenNodes = (type_children: string) => {
        tableheaderArr.forEach((e, idx) => {
          if (e.type === type_children && !indexChildrenArr.includes(idx)) { // 找到节点后并且下标不重复
            indexChildrenArr.push(idx)
            tableheaderArr[idx].clickState = false
            if (e.type_children) { // 如果子节点里还存在后代子节点
              eachAllChildrenNodes(e.type_children)
            }
          }
        })
        return
      }
      eachAllChildrenNodes(type_children)  // 递归遍历当前表头所有后代子节点
    }
  } else {
    // console.log('打开操作')
    showMark = true
    if (type_children) { //如果点击的表头存在子节点
      tableheaderArr.forEach((e, index) => {
        if (e.type === type_children) { //找到节点后
          indexChildrenArr.push(index)
        }
      })
    }
  }
  // console.log(indexChildrenArr)
  indexChildrenArr.forEach(e => { // 更变相关子节点的显示状态
    tableheaderArr[e].show = showMark
  })
  tableheader.value = JSON.parse(JSON.stringify(tableheaderArr))  // 更新表头状态，使用toRaw后不JSON化数据不会时时更新
  tableheader.value[clickIndex].clickState = !tableheader.value[clickIndex].clickState //切换当前表头的点击转态
}
</script>

<style scoped lang="scss">
.box {
  margin: auto;
  width: 80%;
}

.table:deep() {
  td {
    font-size: 13px;
    color: black;
  }

  tr {
    th {
      .cell {
        padding: unset;
        white-space: nowrap;
        text-overflow: unset;
        box-sizing: border-box;
      }
    }
  }

  tr:nth-child(1) {
    th {
      font-size: 14px;
      font-weight: bolder !important;

      .cell {
        padding: 0 20px !important;
      }
    }
  }

  tr:nth-child(2) {
    th {
      text-align: center;
      font-size: 12px;
    }
  }

  // 表头颜色
  .color_0 {
    position: relative;
    color: white !important;
    background-color: goldenrod !important;
  }

  .color_1 {
    color: white !important;
    background-color: seagreen !important;
  }

  .color_2 {
    color: white !important;
    background-color: gray !important;
  }

  .color_3 {
    color: black !important;
    background-color: silver !important;
  }

  // 表头关闭时图标
  .header-close::after {
    content: '▷';
    position: absolute;
    top: 50%;
    left: 4px;
    transform: translate(0, -50%);
    transition: .2s;
    font-size: 16px;
  }

  // 表头打开时图标
  .header-open::after {
    content: '▷';
    position: absolute;
    top: 50%;
    left: 4px;
    transform: translate(0, -50%) rotate(90deg);
    transition: .2s;
    font-size: 16px;
  }

  // 表行颜色
  .row-color-1 {
    td {
      background-color: #95d475 !important;
    }
  }

  .row-color-2 {
    td {
      background-color: #d1edc4 !important;
    }
  }

  .row-color-3 {
    td {
      background-color: #f0f9eb !important;
    }

  }

  .el-empty-table {
    position: absolute;
    left: 500px;
  }

  // 调整滚动条宽度
  .el-table__body-wrapper {
    .el-scrollbar__bar.is-horizontal {
      .el-scrollbar__thumb {
        top: -5px;
        height: 10px !important;
      }
    }

    .el-scrollbar__bar.is-vertical {
      .el-scrollbar__thumb {
        left: -5px;
        width: 10px !important;
      }
    }
  }
}
</style>
